/* * #%L * carewebframework * %% * Copyright (C) 2008 - 2016 Regenstrief Institute, Inc. * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This Source Code Form is also subject to the terms of the Health-Related * Additional Disclaimer of Warranty and Limitation of Liability available at * * http://www.carewebframework.org/licensing/disclaimer. * * #L% */ package org.carewebframework.api.spring; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.BeanDefinitionDecorator; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.context.ApplicationContext; import org.w3c.dom.Attr; import org.w3c.dom.Node; /** * Implementation of DefaultListableBeanFactory that supports namespace extensions to bean * declarations. Specifically, it supports: * <p> * The <b>cwf:override</b> attribute that can explicitly allow or disallow the overriding of a bean * definition, independent of the settings for the application context. It should be applied to the * overriding bean definition. * <p> * The <b>cwf:final</b> attribute that can prevent a bean definition from being overridden under any * circumstances. If set to true, it takes precedence over the application context settings and the * cwf:override setting of any overriding bean definition. The default attribute value is false. */ public class FrameworkBeanFactory extends DefaultListableBeanFactory { private static final String CAN_OVERRIDE_ATTR = FrameworkBeanFactory.class.getName() + ".canOverride"; private static final String IS_FINAL_ATTR = FrameworkBeanFactory.class.getName() + ".isFinal"; private enum BeanOverride { DEFAULT, // Override according to application context setting. NEVER, // Never override. An exception is raised if an existing bean definition is found. ALWAYS, // Always override. Any existing bean definition is replaced. IGNORE // Ignore if a bean definition already exists. No exception is raised. }; /** * This is a decorator for the transferring attribute values from the custom namespace to a * specified metadata attribute on the bean definition. */ private static class Decorator implements BeanDefinitionDecorator { private final String attributeName; /** * Constructor * * @param attributeName The name of the bean definition attribute under which the custom * namespace attribute value will be stored. */ private Decorator(String attributeName) { this.attributeName = attributeName; } @Override public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder, ParserContext ctx) { holder.getBeanDefinition().setAttribute(attributeName, ((Attr) source).getValue()); return holder; } } /** * Namespace handler registers the decorator for the cwf:override attribute. */ public static class NamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { super.registerBeanDefinitionDecoratorForAttribute("final", new Decorator(IS_FINAL_ATTR)); super.registerBeanDefinitionDecoratorForAttribute("override", new Decorator(CAN_OVERRIDE_ATTR)); } } // Default override behavior as dictated by the application context. private boolean defaultOverriding = true; /** * Creates a bean factory. * * @param parentContext Parent application context, if any. When specified, any placeholder * configurers found in the parent context will be registered in this bean factory. * @param parentBeanFactory The parent bean factory, if any. */ public FrameworkBeanFactory(ApplicationContext parentContext, BeanFactory parentBeanFactory) { super(parentBeanFactory); if (parentContext != null) { for (Object configurer : parentContext.getBeansOfType(PlaceholderConfigurerSupport.class, false, false).values()) { registerSingleton("", configurer); } } } /** * The application context will call this to set the override behavior of the bean factory. We * intercept this to set this as the default behavior for the bean factory. This setting is used * when no cwf:override attribute is specified. */ @Override public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { super.setAllowBeanDefinitionOverriding(allowBeanDefinitionOverriding); this.defaultOverriding = allowBeanDefinitionOverriding; } /** * Intercepts the call set the override behavior of the bean factory prior to registering the * bean. The override behavior is set to the cwf:override setting of the bean definition or, in * the absence of this setting, to the default override behavior for the bean factory and by the * cwf:final setting which can be used to prevent a bean declaration from ever being overridden. */ @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { boolean canOverride = defaultOverriding; BeanDefinition oldBeanDefinition = containsBeanDefinition(beanName) ? getBeanDefinition(beanName) : null; String override = oldBeanDefinition == null ? null : getAttribute(beanDefinition, CAN_OVERRIDE_ATTR); String isfinal = oldBeanDefinition == null ? null : getAttribute(oldBeanDefinition, IS_FINAL_ATTR); if (override != null) { switch (BeanOverride.valueOf(override.toUpperCase())) { case ALWAYS: canOverride = true; break; case NEVER: canOverride = false; break; case IGNORE: return; } } if (canOverride && "true".equalsIgnoreCase(isfinal)) { canOverride = false; } super.setAllowBeanDefinitionOverriding(canOverride); super.registerBeanDefinition(beanName, beanDefinition); } /** * Searches this bean definition and all originating bean definitions until it finds the * requested attribute. * * @param beanDefinition Bean definition. * @param attributeName Attribute to locate. * @return The value of the attribute, or null if not found. */ private String getAttribute(BeanDefinition beanDefinition, String attributeName) { String value = null; while (beanDefinition != null) { value = (String) beanDefinition.getAttribute(attributeName); if (value != null) { break; } beanDefinition = beanDefinition.getOriginatingBeanDefinition(); } return value; } }